﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using ASPNETIdentity;
using Eco.Linq;
using Eco.ObjectRepresentation;
using Eco.Services;
using Microsoft.AspNet.Identity;

namespace MVC5
{
  public class UserStoreMDriven:IUserStore<SysUser>,IUserPasswordStore<SysUser>,IUserRoleStore<SysUser>,IUserClaimStore<SysUser>,IUserLoginStore<SysUser>,IUserSecurityStampStore<SysUser>
  {
    private IEcoServiceProvider _sp;
    public UserStoreMDriven(IEcoServiceProvider sp)
    {
      _sp = sp;
    }

    /*IUserStore*/

    System.Threading.Tasks.Task IUserStore<SysUser>.CreateAsync(SysUser user)
    {
      var u = new SysUser(_sp);
      u.Id = Guid.NewGuid().ToString();
      user.Id = u.Id; // So that the transient has the correct id when trying to login
      u.UserName = user.UserName;
      u.PasswordHash = user.PasswordHash;
      u.SecurityStamp = user.SecurityStamp;
      Save();

      return Task.FromResult<object>(null);
    }

    System.Threading.Tasks.Task IUserStore<SysUser>.DeleteAsync(SysUser user)
    {
        user.AsIObject().Delete(); 
        Save();
        return Task.FromResult<object>(null);
    }

    System.Threading.Tasks.Task<SysUser> IUserStore<SysUser>.FindByIdAsync(string userId)
    {
      var list = (from x in _sp.PSQuery<SysUser>() where (x.Id == userId) select x).ToList();
      if (list.Count > 0)
        return Task.FromResult<SysUser>(list[0]);
      return Task.FromResult<SysUser>(null);
    }

    System.Threading.Tasks.Task<SysUser> IUserStore<SysUser>.FindByNameAsync(string userName)
    {
      var list = (from x in _sp.PSQuery<SysUser>() where (x.UserName == userName) select x).ToList();
      if (list.Count > 0)
        return Task.FromResult<SysUser>(list[0]);
      return Task.FromResult<SysUser>(null);

    }

    System.Threading.Tasks.Task IUserStore<SysUser>.UpdateAsync(SysUser user)
    {
          SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
          if (theuser != null)
          {
            theuser.UserName = user.UserName;
            Save();
          }
        return Task.FromResult<object>(null);
    }

    void IDisposable.Dispose()
    {
      _sp = null;
    }

    /*IUserPasswordStore*/

    Task<string> IUserPasswordStore<SysUser>.GetPasswordHashAsync(SysUser user)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        return Task.FromResult<string>(theuser.PasswordHash);
      }
      return Task.FromResult<string>("");
    }

    Task<bool> IUserPasswordStore<SysUser>.HasPasswordAsync(SysUser user)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        return Task.FromResult<bool>(!string.IsNullOrEmpty( theuser.PasswordHash));
      }
      return Task.FromResult<bool>(false);
    }

    Task IUserPasswordStore<SysUser>.SetPasswordHashAsync(SysUser user, string passwordHash)
    {
      user.PasswordHash = passwordHash;
      return Task.FromResult<object>(null);
    }

    /*IUserRoleStore*/


    Task IUserRoleStore<SysUser>.AddToRoleAsync(SysUser user, string role)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        var roles = (from x in _sp.PSQuery<SysRole>() where (x.Name == role) select x).ToList();
        if (roles.Count > 0)
          theuser.SysRoles.Add(roles[0]);
        else
          theuser.SysRoles.Add(new SysRole(_sp) { Name = role });
        Save();

      }
      return Task.FromResult<object>(null);

    }

    Task<IList<string>> IUserRoleStore<SysUser>.GetRolesAsync(SysUser user)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
        return Task.FromResult<IList<string>>((from x in theuser.SysRoles select (x.Name)).ToList());

      return Task.FromResult<IList<string>>(new List<string>());

    }

    Task<bool> IUserRoleStore<SysUser>.IsInRoleAsync(SysUser user, string role)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
        return Task.FromResult<bool>((from x in theuser.SysRoles where (x.Name == role) select (x.Name)).ToList().Count > 0);
      return Task.FromResult<bool>(false);
    }

    Task IUserRoleStore<SysUser>.RemoveFromRoleAsync(SysUser user, string role)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        var sysRole=(from x in theuser.SysRoles where (x.Name == role) select (x)).First();
        if (sysRole!=null)
          theuser.SysRoles.Remove(sysRole);
        Save();

      }
      return Task.FromResult<object>(null);

    }
    /*IUserClaimStore*/

    Task IUserClaimStore<SysUser>.AddClaimAsync(SysUser user, System.Security.Claims.Claim claim)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        var trytofind = (from x in theuser.SysUserClaims where (claim.Issuer == x.Issuer && claim.Type == x.ClaimType) select x).First();
        if (trytofind == null)
        {
          theuser.SysUserClaims.Add(new SysUserClaim(_sp) {ClaimType=claim.Type,ClaimValue=claim.Value,Issuer=claim.Issuer,ValueType=claim.ValueType,OriginalIssuer=claim.OriginalIssuer });
          Save();
        }
      }
      return Task.FromResult<object>(null);
    }

    Task<IList<System.Security.Claims.Claim>> IUserClaimStore<SysUser>.GetClaimsAsync(SysUser user)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        return Task.FromResult<IList<System.Security.Claims.Claim>>((from x in theuser.SysUserClaims select (new Claim(x.ClaimType, x.ClaimValue, x.ValueType, x.Issuer, x.OriginalIssuer))).ToList());
      }
      return Task.FromResult<IList<System.Security.Claims.Claim>>(new List<System.Security.Claims.Claim>());
    }

    Task IUserClaimStore<SysUser>.RemoveClaimAsync(SysUser user, System.Security.Claims.Claim claim)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      { 
        var removethis=(from x in theuser.SysUserClaims where (claim.Issuer==x.Issuer && claim.Type==x.ClaimType) select x).First();
        if (removethis!=null)
          removethis.AsIObject().Delete();
        Save();

      }
      return Task.FromResult<object>(null);
    }
    /*IUserLoginStore*/

    Task IUserLoginStore<SysUser>.AddLoginAsync(SysUser user, UserLoginInfo login)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        theuser.SysExternalLogins.Add(new SysExternalLogin(_sp) { LoginProvider = login.LoginProvider, ProviderKey = login.ProviderKey });
        Save();
      }
      return Task.FromResult<object>(null);
    }

    Task<SysUser> IUserLoginStore<SysUser>.FindAsync(UserLoginInfo login)
    {
      var pk = login.ProviderKey;
      var list = (from x in _sp.PSQuery<SysExternalLogin>() where (x.ProviderKey == pk) select x).ToList();
      if (list.Count > 0)
      {
        return Task.FromResult<SysUser>(list[0].SysUser);
      }
      return Task.FromResult<SysUser>(null);
    }

    Task<IList<UserLoginInfo>> IUserLoginStore<SysUser>.GetLoginsAsync(SysUser user)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        return Task.FromResult<IList<UserLoginInfo>>((from x in theuser.SysExternalLogins select (new UserLoginInfo(x.LoginProvider, x.ProviderKey))).ToList());
      }
      return Task.FromResult<IList<UserLoginInfo>>(null);
    }

    Task IUserLoginStore<SysUser>.RemoveLoginAsync(SysUser user, UserLoginInfo login)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        var theoneToDelete=(from x in theuser.SysExternalLogins where (x.ProviderKey==login.ProviderKey) select ( x)).First();
        if (theoneToDelete != null)
          theoneToDelete.AsIObject().Delete();
      }
      return Task.FromResult<object>(null);

    }
    /*IUserSecurityStampStore*/
    Task<string> IUserSecurityStampStore<SysUser>.GetSecurityStampAsync(SysUser user)
    {
      SysUser theuser = (this as IUserStore<SysUser>).FindByIdAsync(user.Id).Result;
      if (theuser != null)
      {
        return Task.FromResult<string>(theuser.SecurityStamp);
      }
      return Task.FromResult<string>("");
    }

    Task IUserSecurityStampStore<SysUser>.SetSecurityStampAsync(SysUser user, string stamp)
    {
      user.SecurityStamp = stamp;
      return Task.FromResult<object>(null);
    }

    private void Save()
    {
      EcoServiceHelper.GetPersistenceService(_sp).UpdateDatabaseWithList(EcoServiceHelper.GetDirtyListService(_sp).AllDirtyObjects());

    }
  }
}
